perm filename WCCF.4[P,JRA] blob sn#556271 filedate 1981-01-12 generic text, type C, neo UTF8
COMMENT āŠ—   VALID 00002 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002	               GUIDELINES FOR CHOOSING AN OBJECT-ORIENTED PROGRAMMING STYLE IN LISP
C00046 ENDMK
CāŠ—;
               GUIDELINES FOR CHOOSING AN OBJECT-ORIENTED PROGRAMMING STYLE IN LISP

                                          Jim Schmolze
                                   Bolt Beranek and Newman Inc.
                                     Cambridge, Massachusetts


  Abstract: Given a "hybrid" Lisp system which
  includes   an   object-oriented  programming
  facility, some guidelines are presented  for
  choosing   the  object-oriented  style  over
  "pure Lisp".  These guidelines are presented
  in  the  context  of  a  particular  object-
  oriented    programming    system,    called
  TINYTALK, which is briefly described.



1. Should you read this paper?

  This paper is written from the perspective  of
a  programmer or system designer who is about to
design and construct a Lisp program. Its  intent
is  to  provide  useful  guidelines for deciding
when an object-oriented design is preferable  to
"pure Lisp". The quotations just used are simply
a  reminder  that  the  various  Lisp  languages
available today differ immensely,  making  "pure
Lisp"  a  vague  term.  It  will be used in this
paper to include all of Lisp except for  object-
oriented facilities.


  An implicit assumption of this paper is that a
Lisp  system with an object-oriented programming
facility is available to the designer, making it
possible  to  produce  either   Lisp   programs,
object-oriented programs or a hybrid of the two.
Of  course,  you may not have an object-oriented
facility in your Lisp, but maybe someday ...


  Before we continue, let us  simplify  some  of
the  notation.  The  word  "object" will replace
                             1
"object-oriented programming"  and "Lisp-object"
                                               2
will replace "Lisp object-oriented programming".


  Prior to presenting these guidelines, a  brief
description  of  a particular Lisp-object system
will be given. The  author  built  this  system,
called  TINYTALK, for the variant of Lisp called
INTERLISP [Teitelman 78].  TINYTALK will provide
a useful context for the arguments contained  in
this paper.


  The  advice  offered in this paper arises from
the  author's  experience  with  designing   and
writing    several   programs   in   Lisp-object
environments over the course of  a  year,  after
several   years   of   working  in  "pure  Lisp"
environments.    The  hybrid  environments  were
INTERLISP  with  TINYTALK  and MACLISP [Moon 74]
with the EXTEND package [Kerns 80].



2. A brief description of TINYTALK

  The original goal for TINYTALK was to  provide
a   simple   test-bed  for  writing  Lisp-object
programs, hence it is still incomplete. However,
it does include  several  features  found  among
other  object  systems.  The  primary influences
were SMALLTALK [Goldberg&Kay 76, Ingalls 78] and
the EXTEND package of MACLISP.


  The basic structure of TINYTALK allows:

  o  definition of classes

  o  creation of instances of  a  class  and
     access to instance variables

  o  applying   operators  to  an  instance,
     where each operator must be defined for
     the class of the instance


  The programmer creates a hierarchy of  classes
which  form  an  acyclic graph.  Every class has
one  or  more  parent  class.  The   system   is
initialized  with  a distinguished class, called
"Thing". If no parent is explicitly stated for a
class, its parent is the class  "Thing".  Hence,
the  Thing  class  is  the  root  class  of  the
hierarchy, where Thing has no parent class.


  The  hierarchy  of  classes  is  an  important
design consideration because subordinate classes
inherit  instance  variable  names  and operator
definitions from their parents. The next section
will explain these terms in detail.


2.1. Defining classes

  A class definition consists of:

  o  a name -- which is merely a  convenient
     device for referring to a class.

  o  a  set  of parent classes -- this forms
     the class hierarchy.

  o  a  set  of  locally  defined   instance
     variable  names  --  each instance of a
     class will have a slot corresponding to
     each  variable  name   in   the   class
     definition.  These  slots  are  able to
     store any Lisp value. The complete  set
     of  instance variable names for a class
     is the union  of  its  locally  defined
     variable  names  plus  those of all its
     parents.

  o  a  set  of  locally  defined   operator
     definitions   --   the  inheritance  of
     operators  is  similar   to   that   of
     instance  variable  names.  If  a local
     operator definition  and  an  inherited
     one  have  the same name, the local one
     overrides. This allows operations to be
     redefined   for    children    classes.
     Additionally,  if  two  parent  classes
     have operators with the same name,  the
     child  class  inherits  the  definition
     from the "first"  parent  class,  where
     the  order  of  the parent classes is a
     part of declaring the parents.


  Each operator definition is stored as  a  Lisp
function. Given the pair, <class,operator-name>,
a  Lisp  function  name  is  generated  and  the
definition is stored there. Each definition  can
have   any   number   of   arguments   with  the
requirement  that  the  first  argument  be  the
instance   to   which  the  operation  is  being
applied.


2.2. Example: A Window Package

  An example, which will now be introduced, will
be used throughout this paper. It is part  of  a
program  the  author  wrote  using INTERLISP and
TINYTALK.    The  window  package  is  a  simple
program  for manipulating terminals which have a
                  3
window capability.  It allows a user  to  define
an arbitrary number of windows and activate them
whenever   desired  for  printing  purposes.  An
excellent   example   of   window    usage    is
DLISP   [Teitelman   77],   which   provides  an
extremely sophisticated window package.


  Part of the class  hierarchy  for  the  window
package is:

Thing -- vars: <none>
  ↑       ops: (PrintVars self)
  |
Window -- vars: X0,Y0,Width,Height,Color
  ↑        ops: (Move self x0 y0)
  |             (Grow self width height)
  |             (Activate self)
  |             (Clear self)
  |             (Draw self)
  |
WorkWindow -- vars: Title,TitleWindow,
                    TextWindow
               ops: (Activate self)
                    (Clear self)
                    (Draw self)
                    (BlinkTitle self)


  The  class  "Thing"  is  defined by the system
with no  instance  variables  and  one  operator
which  simply prints the values of all variables
in an instance. Hence, all classes  inherit  the
operator  "PrintVars". Notice that each operator
is listed with the  arguments  it  expects,  the
first  of  which is always the instance to which
the operator is being applied.


  The class "Window" is a child of Thing and has
5 instance variables for its origin (X0 and Y0),
size (Width and Height) and  color.  The  "Move"
and "Grow" operators simply modify the values of
some  of these variables.  "Activate" will cause
all terminal output to be printed in  the  given
window.   "Draw" will completely draw the window
by "Activate"-ing it and "Clear"-ing it.


  The class "WorkWindow" is  intended  to  be  a
window  with  a  title  on  top.    The variable
"Title" holds the text of the  title,  which  is
printed    inside    the    window   stored   in
"TitleWindow". "TextWindow" stores the window in
which normal  output  appears.  Notice  that  an
instance  of WorkWindow is also a window, namely
it inherits an X0, Y0, etc. Also it inherits the
operator  definitions  for  "Grow"  and  "Move".
However,  it redefines the operators "Activate",
"Clear"  and  "Draw"  since  they   differ   for
WorkWindows  from  other  windows  (i.e.  "Draw"
must  print  the  title,  etc.).  The   operator
"BlinkTitle"  is  added  to WorkWindows. Such an
operator doesn't make sense  for  other  windows
since they don't have titles.


2.3. Creating Instances and Accessing their
     Variables

  Instances of any class may be created with the
Lisp function 

    (CreateInst class)

which  returns a new instance with all variables
initialized  to  NIL.  The   instance   contains
storage  slots  for  all instance variable names
associated  with  the  class.  The   values   of
instance variables can be retrieved via 

    (getv instance var-name)

and can be set via 

    (setv instance var-name value)

Using  the  Window  class  example,  let w be an
instance of the Window class.  

                   4
    (setv w 'X0 10)  returns 10, after which
    (getv w 'X0) also returns 10


  The access functions  for  instance  variables
could   just   as   well   have   been  done  by
automatically adding operators for each variable
when the class was defined. The choice of  using
getv  and setv was made simply because they were
easy to implement.  They  do  not  constitute  a
permanent part of the language.


2.4. Applying Operators to Instances

  An operator is applied to an instance via

    (send instance op-name . arguments)


  The  class  of  the the instance together with
                                               5
the op-name maps directly into a Lisp  function 
(see section on Defining Classes). This function
is then called as 

    (fn instance . arguments)

The value of the "send" is the value returned by
the evaluation of the operator definition.


  Using  our  example,  once  again,  if w is an
instance of the Window class:

    (send w 'Move 10 20) is evaluated as
    (Move-fn-for-Window w 10 20)


  which will set the X0 of w to 10 and its Y0 to
20.


  This concludes the brief overview of TINYTALK.



3. Guidelines for Choosing Object-Oriented
   Programming Style

  Before launching into guidelines, a  basis  of
programming  methodology  will be presented. The
following comments about the design process  for
a  software  system  are  certainly not provably
correct. However, they constitute  the  author's
(and    others)    combination   of   structured
programming techniques and common sense.


3.1. The Intentional Design of a Software System

  The title of  this  section  may  have  rather
esoteric "ring" to it, however, a simple meaning
is  intended.  First, let us assume that we wish
to design a program which has a reasonably  well
specified  behavior.  During the design process,
several  design  goals   will   necessarily   be
achieved,  whether  the program is to be written
in Lisp, Lisp-object,  PASCAL,  FORTRAN  or  any
other language. Specifically, these goals are:


  1.  Design  of  the  intent  of  the  data
                6
      structures  required for the program.

 2.  Design  of  the  relationships  between
      these data structures.

 3.  Design  of  the intent of operations to
      be   performed   upon    these    data
      structures  and  the algorithms needed
      to define them.


  Of course,  these  design  goals  may  not  be
achieved  for the entire program simultaneously.
In fact, the author's experience shows that  the
design  of a system grows gradually, changing as
the system is constructed.    However,  for  the
purposes  of  the  following discussion, we will
assume that the intentional  design,  which  has
just  been described, has been completed for the
program being planned.


3.2. Guideline 1: Counting unique data
     structures and operations

  Two questions are relevant at this point:

    Q1: What are the unique types of data
        structures in the design?

    Q2: What are the unique types of
        operations for all data structures?


  Given the fact that data structures are easily
modeled by class instances, one  is  tempted  to
think  that  if there are lots of different data
structures,   then   object    programming    is
preferrable.  Also,  since  operator definitions
are similar to Lisp functions, if there are lots
of different operators,  then  pure  Lisp  seems
more appropriate. Unfortunately, the mere totals
of  data  structure types and operators offer no
real guidance at all because both  of  them  are
easily defined in either object or Lisp systems.


  In fact, the answers to these questions do not
provide  obvious  guidelines at all, but they do
provide a useful framework for the next section.


3.3. Guideline 2: Similar operations for
     different data structures

    Q3: How often do several data
        structures have similar or identical
        operators?

    Q4: Of these operators, which have
        identical algorithms for all the
        data structures which use them?

  The answers to these questions  determine  the
amount  of  "operator overlap" that occurs among
the data structures. Q3 is  concerned  with  the
overlap   of  the  intention  of  operators;  Q4
addresses   the   overlap   of    definitionally
equivalent operators.


  In  the  window  package  example,  the set of
operators for both  Window  and  WorkWindow  are
nearly the same. However, for some of them, such
as  "Draw",  the  actual  code  to  perform  the
operation  will  differ  for  each  class  while
operators   such   as   "Move"   are  programmed
identically for both.


  If there is much operator overlap,  there  are
two  reasons for choosing object design.  First,
in the object design, the  programmer  needs  to
use  only  one  name for the operator whereas in
pure Lisp, a different name (i.e.   a  different
Lisp  function)  is needed for each variant that
must be coded  differently.  Using  our  example
again, the "Draw" operation for Windows is coded
differently than "Draw" for WorkWindows, but the
operator  "Draw" works for both of them. In pure
Lisp, two names  (i.e.  functions)  are  needed,
such as, "DrawWindow" and "DrawWorkWindow".


  Second,  in  object  design,  the  part of the
software that calls for an  operation,  such  as
"Draw", does not need to know whether the window
is  a  normal Window or a WorkWindow because the
system  automatically   uses   the   appropriate
definition  from  the  class of the instance. In
pure Lisp, one must determine the  type  of  the
data  structure so the correct draw function can
be called.


  The second argument presented here is actually
a general feature of  object  systems.  However,
this  feature is especially useful when there is
much operator overlap. An  important  disclaimer
to  note is that if the actual definition for an
operator is identical  for  each  type  of  data
structure,   these   two   arguments   are   not
applicable and this guideline  becomes  useless.
In  that  situation, only one function is needed
in pure Lisp to perform  the  operation  and  it
could  be  used  for  all  of  the types of data
structures.


  It must also be noted that no claim  is  being
made about the total amount of software required
to  write  programs  for  which  this  guideline
applies. In fact, in the author's  opinion,  the
program's  size  for  either  pure Lisp or Lisp-
object systems is approximately  the  same.  The
critical  difference is with respect to the ease
and  simplicity  of   designing,   writing   and
debugging software.


3.4. Guideline 3: Utilizing the inheritance
     between classes

  Rather  than  enumerating the proper questions
that  should  be  asked,  we  will  examine  the
properties  of  inheritance to discover criteria
for determining its utility for  any  particular
program.  The  thrust of this guideline is to be
able to recognize program designs for which  the
class  inheritance  of  an  object system offers
distinct advantages over pure Lisp.


  The first step requires an examination of  the
relationships  between  data  structures  in our
intentional design. In particular, we  look  for
relationships that fit well into the paradigm of
a  class hierarchy for object systems. There are
two properties to seek. The easiest  is  one  of
subsumption;  i.e.  is one data structure a more
specific version of another where  the  size  of
the  data  structure  is simply extended and not
modified.  This type of relationship  forms  the
parent-child class relationship. The second type
is   the   composition   of  two  or  more  data
structures into a new structure,  possibly  with
extensions. This type of relationship also forms
a  parent-child  relationship  but with multiple
parents.


  An important item to remember when  performing
this exercise is that inheritance works for both
structure   and   operators,  so  they  must  be
considered together. The analysis performed  for
the  previous  guideline will be useful here. If
there is "operator overlap" between classes that
                                7
have  an  ancestor  relationship   and  if   the
operators which overlap are defined identically,
then  by  placing the operator definition at the
               8
"higher" class,  the ancestor will  inherit  the
operator  and  its definition. The more of these
situations one finds, the better  the  case  for
choosing object design.


3.5. Guideline 4: Do you "like" object-oriented
     programming?

  The  question  of "liking" the object style is
probably  the  crucial  question  for   deciding
between   object  style  versus  pure  Lisp.  Of
course, this decision is  intended  to  be  made
with  respect  to  the  particular program being
designed. But what is  meant  by  "liking"?  The
point  of the question is to get the designer to
consider   whether   the   object    style    is
appropriate.    Is  it  a comfortable and useful
paradigm  for  the  design?  Furthermore,  which
portions  of  the  program  are  best  done with
object style and which are well suited for  pure
Lisp?


  Unfortunately,  the  only  way  to  be able to
answer such subjective questions is to have  had
experience  with both types of environments. The
next section of this  paper  will  discuss  some
general  advantages  of  object  design and will
provide some information for making the  choice.
However,  it  is  the  author's  conviction that
experience  is  necessary  to  make  the  proper
choice.



4. General Advantages of Object-Oriented
   Programming over Lisp (plus a tiny little
   disadvantage)

  Most  of  the  advantages  presented here deal
with aiding the designer and programmer  in  the
organization  of  the  program. First of all, an
object system provides  a  partitioning  between
data  structures,  along  with  their operators.
Each type of data structure and  the  operations
that  can be performed upon it are automatically
grouped together  by  the  system,  providing  a
useful  tool  for viewing the modular components
of the program.


  Additionally, the intentional  design  of  the
program,  described  earlier in the paper, has a
partial physical  reification  in  the  program.
Data structures and their operators plus some of
the   relationships   between   structures   are
explicitly represented in  the  actual  program.
This  allows  the  design to be reflected in the
program more completely than could  be  done  in
pure Lisp.


  A  small advantage of object systems over some
variants of Lisp is that when one defines a data
structure in an object system  (i.e.  a  class),
the  system automatically provides functions for
creating, accessing and modifying  instances  of
the  data  structure.  In TINYTALK, these system
functions   are,   respectively,   "CreateInst",
"getv"  and "setv"; all of which can be used for
any class. Hence, the programmer need not define
such functions for each type of data  structure.
Some   versions  of  Lisp,  such  as  INTERLISP,
                             9
already have such facilities. 


  More importantly, the object system forces the
programmer  into  the  philosophy  of  creating,
accessing  and  modifying  instances  through  a
functional interface that is independent of  the
actual   structure  of  the  instance.  Instance
variables can only be accessed by name. For  the
reader  who  is  familiar with database systems,
this is roughly analogous  to  creating  a  data
model  which  resides  between  the system being
built and the physical structure  of  the  data.
This  particular  type of modularity can also be
achieved  in  pure  Lisp,  but  only  when   the
programmer chooses to do so.


  The final advantage to be mentioned deals with
insulating  the  instances of a class. There are
two ways to modify the state of an instance. One
is to apply some operation  which  modifies  it;
the  other  is to change its instance variables.
It is possible to create an object system  which
did  not  allow  instance  variable modification
from operators outside of the class  definition.
TINYTALK  does not have this restriction, but it
could  be  extended  as  such.  Given   such   a
property,  the state of all instances of a class
would be insulated from software  not  contained
in the class definition because all modification
must  go  through  a  class operator. Of course,
other  software  in  the  system  could  perform
operations    incorrectly,    thereby    causing
difficulties. But  the  modularity  provided  by
such  insulation  prevents  the  problems due to
external  interference   with   the   state   of
instances.


  The tiny little disadvantage mentioned in this
section's title is, unfortunately, not so small.
Execution  speed  of  object  programs is slower
than for  pure  Lisp.  Each  application  of  an
operator requires an examination of the instance
to  find  its class; a table-lookup in the class
to  find  the  appropriate  function   for   the
operator;   and   an  application  of  the  Lisp
function.    In  pure  Lisp,  only  the   actual
function  call  is  necessary.    Second, when a
portion of code which  applies  an  operator  is
                                              10
compiled,  it  can  never  be compiled in-line  
because the actual code to be executed cannot be
determined until execution time. In  pure  Lisp,
the  actual  code  to  be  executed can often be
determined at compilation time, allowing in-line
compilation when desired.


  Similar costs are incurred for  each  instance
variable   access,  since  the  access  function
depends upon the class of the instance  as  well
as   the   variable  name.  The  author  has  no
statistics upon the loss in execution  time  but
feels  that  they  are  small  compared to other
gains offered by object systems.



5. Conclusions

  Since the bulk of this paper is concerned with
summarizing the author's intuitions about  Lisp-
object programming, it would be inappropriate to
include  factual  information in the conclusion.
Hence, let us briefly  state  what  is  probably
obvious by now.


  No  claim is made about the intrinsic value of
the object-oriented programming  style.  Nor  is
any  claim made about any particular programming
language paradigm. Instead,  the  author  claims
that  object-oriented  programming  makes system
design  and  implementation  easier   for   some
classes  of  tasks. The guidelines presented are
intended to aide a system designer  in  deciding
when  his  particular  program  fits into such a
class.


  Once  again,  the   "ease"   of   design   and
implementation just mentioned refers to reducing
the conceptual complexity of the system. For the
author, this type of reduction has the effect of
reducing the number of hours spent designing and
debugging a program.


  Finally, although the context of this paper is
to contrast Lisp programming with and without an
object-oriented   facility,   nothing  presented
depends  critically  upon  the  Lisp   language,
except one. Namely, Lisp allows the introduction
of an object system and, furthermore, works well
with  a  hybrid environment. These arguments and
guidelines may well  be  appropriate  for  other
computer  languages  offering an object-oriented
facility.



                   FOOTNOTES

  1
   Note that I have resisted using  the  acronym
OOP.

  2
   Yes,  you  guessed  it  -- it could have been
LOOP.

  3
   A window capability on a terminal  allows  an
arbitrary  rectangle  to  become  the  region in
which text is written and scrolled, just  as  if
it were the entire screen.

  4
   The  X0  is quoted because setv evaluates its
arguments.

  5
   Unless  the  class  does  not  have  such  an
operator, in which case a Lisp error occurs.

  6
   The  usage  of "data structure" is an attempt
to get away from  language-specific  terminology
such as "instance" or "list".

  7
   An   ancestor  relationship  between  classes
means there is a  chain  of  parent-child  class
relationships the connect the two given classes.

  8
   A  "high"  class  is one that is close to the
root of the class hierarchy, namely,  the  Thing
class.

  9
   This facility is called the record package in
INTERLISP.

  10
    This   is   called   open   compilation   in
INTERLISP.



                   REFERENCES

[Goldberg&Kay 76]
               Goldberg, A., Kay, A.
               SMALLTALK-72 Instruction Manual
               Xerox Palo Alto Research Center,
                  Palo Alto, California, 1976.

[Ingalls 78]   Ingalls, D.
               The SMALLTALK-76 Programming
                  System Design and
                  Implementation.
               In Proceedings of the Fifth
                  Annual ACM Symposium on
                  Principles of Programming
                  Languages.  ACM, Tucson,
                  Arizona, January 23-25, 1978.

[Kerns 80]     Kerns, R. W.
               EXTEND package for MACLISP.
               Massachusetts Institute of
                  Technology Artificial
                  Intelligence Laboratory.
               Personal Communication.

[Moon 74]      Moon, D.
               MACLISP Reference Manual
               Revision 0 April 1974 edition,
                  Project MAC, Massachusetts
                  Institute of Technology,
                  Cambridge, Massachusetts,
                  1974.

[Teitelman 77] Teitelman, W.
               A Display Oriented Programmer's
                  Assistant.
               In Proceedings of the Fifth
                  International Joint Conference
                  on Artificial Intelligence,
                  pages 905-915.  IJCAI,
                  Cambridge, Massachusetts,
                  1977.

[Teitelman 78] Teitelman, W., Goodwin, J.W.,
               Hartley, A.K., Lewis, D.C.,
               Vittal, J.J., Yonke, M.D.,
               Bobrow, D.G., Kaplan, R.M.,
               Masinter, L.M., Sheil, B.A.
               INTERLISP Reference Manual
               Revised October 1978 edition,
                  Xerox Palo Alto Research
                  Center, Palo Alto, California,
                  1978.


-------